<template>
  <div id="WebGL-output"></div>
</template>

<script setup>
import {getCurrentInstance, onMounted, reactive} from 'vue';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls';
import {gui as dat} from 'dat.gui';

let {proxy} = getCurrentInstance();
let THREE = proxy.$THREE;

let scene = reactive({})
let camera = reactive({})
let renderer = reactive({})
let cube = reactive({})
let sphere = reactive({})
let spotLight = reactive({})
let plane = reactive({})
let axesHelper = reactive({})
let gridHelper = reactive({})
let controls = reactive({})
let sphereMaterial = reactive({})
let planeMaterial = reactive({})

// 初始化
function initThree() {
  // 创建场景
  scene = new THREE.Scene();
  // 创建照相机
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
  // 配置相机
  camera.position.set(200, 150, 200);// 相机在三维空间的位置
  // camera.lookAt(scene.position); // 将相机指向场景
  camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相机看向空间坐标原点

  // 创建渲染器 (画布)
  renderer = new THREE.WebGLRenderer();
  // 配置渲染器
  renderer.antialias = true;// 抗锯齿
  renderer.autoClear = true;// 自动清除
  renderer.setClearColor(0x050505); // canvas画布颜色
  renderer.setSize(window.innerWidth, window.innerHeight); // canvas 画布大小
  renderer.shadowMap.enabled = true; // 让渲染器支持投影

  //辅助线 X轴是红色. Y轴是绿色. Z轴是蓝色
  axesHelper = new THREE.AxesHelper(100);
  scene.add(axesHelper);

  // 创建网格辅助
  gridHelper = new THREE.GridHelper(1200, 50, 0xFF4444, 0x404040);

  createPlane(); //地面
  createSphere(); //球体
  createCube(); //立方体
  createLight(); //灯光
  createPoint(); //光源

  // 绕轴旋转
  const pivotPoint = new THREE.Object3D();
  pivotPoint.add(sphere);
  cube.add(pivotPoint);

  // 将这个canvas元素插入到 html中
  document.getElementById('WebGL-output').appendChild(renderer.domElement);
  renderer.render(scene, camera)
}

// 创建立方体
function createCube() {
  let cubeGeometry = new THREE.BoxGeometry(50, 50, 50); //长宽高
  let cubeMaterial = new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}); //材质颜色
  cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
  cube.position.x = 20; //三轴的位置
  cube.position.y = 40;
  cube.position.z = -20;
  cube.castShadow = true; //立方体投影
  scene.add(cube);
}

// 创建球体
function createSphere() {
  let sphereGeometry = new THREE.SphereGeometry(25, 20, 20); //半径、宽高
  sphereMaterial = new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}); //材质颜色
  sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
  sphere.position.x = 20; //三轴的位置
  sphere.position.y = 30;
  sphere.position.z = -20;
  sphere.castShadow = true; //立方体投影
  scene.add(sphere);
}

// 创建灯光
function createLight() {
  const ambientLight = new THREE.AmbientLight({color: 0x404040});
  scene.add(ambientLight);
}

// 添加光源
function createPoint() {
  spotLight = new THREE.SpotLight(0xcccccc); //设置灯光的颜色
  spotLight.position.set(-100, 300, 10); //点光源位置
  // spotLight.angle = Math.PI / 10; //设置投影的程度
  spotLight.shadow.mapSize.set(2048, 2048);     // 定义阴影的分辨率
  spotLight.castShadow = true; //灯光投影
  scene.add(spotLight);
}

// 创建地面
function createPlane() {
  let planeGeometry = new THREE.PlaneGeometry(400, 400);   //地面的材质，宽高
  planeMaterial = new THREE.MeshLambertMaterial({color: 0x6D6565});  // 创建地面
  plane = new THREE.Mesh(planeGeometry, planeMaterial); // 设置地面的位置
  plane.receiveShadow = true; //地面被投影

  // 绕x轴旋转90度
  plane.rotation.x = -0.5 * Math.PI;
  plane.position.x = 0;
  plane.position.y = 0;
  plane.position.z = 0;
  scene.add(plane);  //将地面添加到坐标轴中
}

function initControls() {
  controls = new OrbitControls(camera, renderer.domElement)
  // 立方体可调节属性
  controls.cubeRotationSpeed = 0.01
  controls.cubePositionX = 20
  controls.cubePositionY = 40
  controls.cubePositionZ = -20

  // 球方体可调节属性
  controls.spherePositionX = 20
  controls.spherePositionY = 30
  controls.spherePositionZ = -20
  controls.sphereWireframe = sphereMaterial.wireframe // 网格几何体
  controls.sphereColor = sphereMaterial.color.getStyle();

  // 平面可调节属性
  controls.planeColor = planeMaterial.color.getStyle()

  // 场景可调节属性
  // controls.sceneBackground = renderer.getClearColor().getHex()
  controls.spotLight = true
  controls.cube = true
  controls.sphere = true
  controls.plane = true
  controls.axesHelper = false
  controls.gridHelper = false
  controls.selectMesh = 'cube'
}

// 初始化GUI
function initGUI() {
  // 创建图形调试控件
  const gui = new dat.GUI({autoPlace: false})
  // 添加id
  gui.domElement.id = 'gui-box'
  // 立方对象体调试栏目
  const cubeControls = gui.addFolder('立方体控制')
  cubeControls.add(controls, 'cubeRotationSpeed', 0.01, 100)
  cubeControls.add(controls, 'cubePositionX', -70, 70)
  cubeControls.add(controls, 'cubePositionY', 40, 85)
  cubeControls.add(controls, 'cubePositionZ', -100, 100)
  cubeControls.add(controls, 'cubePositionX').listen()
  cubeControls.add(controls, 'cubePositionY').listen()
  cubeControls.add(controls, 'cubePositionZ').listen()
  // 球体对象调试栏目
  const sphereControls = gui.addFolder('球体控制')
  sphereControls.add(controls, 'spherePositionX', 0, 100)
  sphereControls.add(controls, 'spherePositionY', 0, 100)
  sphereControls.add(controls, 'spherePositionZ', -100, 100)
  sphereControls.add(controls, 'sphereWireframe').onChange((e) => {
    sphereMaterial.wireframe = e
  })
  sphereControls.addColor(controls, 'sphereColor').onChange((e) => {
    sphereMaterial.color.setStyle(e)
  })
  // 地面对象调试栏目
  const planeControls = gui.addFolder('地面控制')
  planeControls.addColor(controls, 'planeColor').onChange((e) => {
    planeMaterial.color.setStyle(e)
  })
  // 场景对象调试栏目
  const sceneControls = gui.addFolder('场景控制')
  /*  sceneControls.addColor(controls, 'sceneBackground').onChange((e) => {
      renderer.setClearColor(e)
    })*/
  sceneControls.add(controls, 'spotLight').onChange((e) => {
    e ? scene.add(spotLight) : scene.remove(spotLight)
  })
  sceneControls.add(controls, 'cube').onChange((e) => {
    e ? scene.add(cube) : scene.remove(cube)
  })
  sceneControls.add(controls, 'plane').onChange((e) => {
    e ? scene.add(plane) : scene.remove(plane)
  })
  sceneControls.add(controls, 'axesHelper').onChange((e) => {
    e ? scene.add(axesHelper) : scene.remove(axesHelper)
  })
  sceneControls.add(controls, 'gridHelper').onChange((e) => {
    e ? scene.add(gridHelper) : scene.remove(gridHelper)
  })
  sceneControls.add(controls, 'selectMesh', ['cube', 'sphere']).onChange((e) => {
    scene.remove(cube)
    scene.remove(sphere)
    switch (e) {
      case 'cube':
        scene.add(cube)
        break;
      case 'sphere':
        scene.add(sphere)
        break;
    }
    scene.add(e);
  })
  // 调整插件布局
  gui.domElement.style.position = 'absolute'
  gui.domElement.style.right = '0px'
  gui.domElement.style.top = '50px'
  document.getElementById('WebGL-output').append(gui.domElement)
}

function render() {
  // 旋转速度动态设置
  cube.rotation.x += controls.cubeRotationSpeed
  cube.rotation.y += controls.cubeRotationSpeed
  cube.rotation.z += controls.cubeRotationSpeed
  // 立方体动态设置位置
  cube.position.x = controls.cubePositionX
  cube.position.y = controls.cubePositionY
  cube.position.z = controls.cubePositionZ
  // 球体动态设置位置
  sphere.position.x = controls.spherePositionX
  sphere.position.y = controls.spherePositionY
  sphere.position.z = controls.spherePositionZ
  // 开始渲染
  renderer.render(scene, camera);
}

function animate() {
  requestAnimationFrame(animate)
  render()
}

// 窗口大小改变触发的方法
function onWindowResize() {
  // 改变相机的 aspect 为窗口的宽和长度之比
  camera.aspect = window.innerWidth / window.innerHeight;
  // 更新相机的投影矩阵
  camera.updateProjectionMatrix();
  // 重新设置渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight);
}

onMounted(() => {
  initThree()
  initControls()
  initGUI()
  animate()
  // 添加事件监听
  window.addEventListener('resize', onWindowResize, false);
})
</script>

<style scoped>

</style>
